/*
	Copyright 2010-2012 Tommaso Previero
	
	This file is part of TeXdocGraphic.

	TeXdocGraphic is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	TeXdocGraphic is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with TeXdocGraphic.  If not, see <http://www.gnu.org/licenses/>.
	
	Created:		12/05/2012

*/

package it.texdocgraphic.gui;

import it.texdocgraphic.interpreter.TexdocOutputInterpreter;
import it.texdocgraphic.interpreter.TexdocResult;
import it.texdocgraphic.logger.SimpleLogger;

import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.Toolkit;

import javax.swing.JFrame;
import java.awt.GridBagLayout;
import javax.swing.JLabel;
import java.awt.GridBagConstraints;
import javax.swing.JTextField;
import java.awt.Insets;

import javax.swing.AbstractButton;
import javax.swing.ButtonModel;
import javax.swing.DefaultListModel;
import javax.swing.JRadioButton;
import javax.swing.JButton;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.SwingConstants;
import javax.swing.JList;
import javax.swing.ButtonGroup;
import javax.swing.UIManager;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.swing.JScrollBar;

public class TeXdocGraphic {

	private JList list;
	private GridBagConstraints gbc_list;
	private JFrame frmTexdocGui;
	private JButton btnOpen;
	private JButton btnClear;
	private JTextField textField;
	private JSeparator separator;
	private GridBagConstraints gbc_separator;
	private JScrollPane jScrollPane;
	private JListTooltip tooltips = new JListTooltip();
	private final ButtonGroup buttonGroup = new ButtonGroup();
	private HashMap<String, ButtonModel> groupMap = new HashMap<String, ButtonModel>();
	private List<TexdocResult> results =  new ArrayList<TexdocResult>();
	
	private TexdocOutputInterpreter toi = new TexdocOutputInterpreter();

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		
		//try to set the look&feel
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch(Exception e) {
			SimpleLogger.log("Failed to set the look&feel");
			SimpleLogger.log(e.getMessage());
		}
		
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					TeXdocGraphic window = new TeXdocGraphic();
					window.frmTexdocGui.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the application.
	 */
	public TeXdocGraphic() {
		initialize();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initialize() {
		frmTexdocGui = new JFrame();
		frmTexdocGui.setTitle("TeXdoc GUI");
		frmTexdocGui.setBounds(100, 100, 500, 300);
		frmTexdocGui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frmTexdocGui.setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("images/icon32.png")));
		GridBagLayout gridBagLayout = new GridBagLayout();
		gridBagLayout.columnWidths = new int[]{104, 104, 10, 245, 0};
		gridBagLayout.rowHeights = new int[]{0, 0, 0, 0, 0, 0, 133, 0};
		gridBagLayout.columnWeights = new double[]{0.0, 0.0, 0.0, 1.0, Double.MIN_VALUE};
		gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, Double.MIN_VALUE};
		frmTexdocGui.getContentPane().setLayout(gridBagLayout);
		
		JLabel lblSearchString = new JLabel("Search string:");
		GridBagConstraints gbc_lblSearchString = new GridBagConstraints();
		gbc_lblSearchString.gridwidth = 2;
		gbc_lblSearchString.anchor = GridBagConstraints.WEST;
		gbc_lblSearchString.insets = new Insets(2, 2, 5, 5);
		gbc_lblSearchString.gridx = 0;
		gbc_lblSearchString.gridy = 0;
		frmTexdocGui.getContentPane().add(lblSearchString, gbc_lblSearchString);
		
		separator = new JSeparator();
		separator.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
		separator.setOrientation(SwingConstants.VERTICAL);
		separator.addMouseListener(new MouseListener() {
			private boolean isShowing = true;
			public void mouseClicked(MouseEvent e) {
				if(isShowing) {
					hideListResult(list.getModel().getSize() > 0);
					isShowing = false;
				} else {
					showListResult();
					isShowing = true;
				}
			}
			public void mouseEntered(MouseEvent e) {}
			public void mouseExited(MouseEvent e) {}
			public void mousePressed(MouseEvent e) {}
			public void mouseReleased(MouseEvent e) {}
		});
		gbc_separator = new GridBagConstraints();
		gbc_separator.gridheight = 7;
		gbc_separator.fill = GridBagConstraints.VERTICAL;
		gbc_separator.insets = new Insets(2, 2, 2, 5);
		gbc_separator.gridx = 2;
		gbc_separator.gridy = 0;
		frmTexdocGui.getContentPane().add(separator, gbc_separator);
		
		textField = new JTextField();
		GridBagConstraints gbc_textField = new GridBagConstraints();
		gbc_textField.gridwidth = 2;
		gbc_textField.fill = GridBagConstraints.HORIZONTAL;
		gbc_textField.insets = new Insets(2, 2, 5, 5);
		gbc_textField.gridx = 0;
		gbc_textField.gridy = 1;
		frmTexdocGui.getContentPane().add(textField, gbc_textField);
		textField.setColumns(10);
		
		JRadioButton rdbtnImmediate = new JRadioButton("Immediate");
		buttonGroup.add(rdbtnImmediate);
		groupMap.put("immediate", rdbtnImmediate.getModel());
		rdbtnImmediate.setSelected(true);
		GridBagConstraints gbc_rdbtnImmediate = new GridBagConstraints();
		gbc_rdbtnImmediate.anchor = GridBagConstraints.WEST;
		gbc_rdbtnImmediate.insets = new Insets(2, 2, 5, 5);
		gbc_rdbtnImmediate.gridx = 0;
		gbc_rdbtnImmediate.gridy = 2;
		frmTexdocGui.getContentPane().add(rdbtnImmediate, gbc_rdbtnImmediate);
		
		JRadioButton rdbtnListSearch = new JRadioButton("List search");
		buttonGroup.add(rdbtnListSearch);
		groupMap.put("list", rdbtnListSearch.getModel());
		GridBagConstraints gbc_rdbtnListSearch = new GridBagConstraints();
		gbc_rdbtnListSearch.insets = new Insets(2, 2, 5, 5);
		gbc_rdbtnListSearch.gridx = 1;
		gbc_rdbtnListSearch.gridy = 2;
		frmTexdocGui.getContentPane().add(rdbtnListSearch, gbc_rdbtnListSearch);
		
		JButton btnSearch = new JButton("Search");
		btnSearch.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg) {
				enableGraphics(false);
				if(buttonGroup.getSelection().equals(groupMap.get("list"))) {
					//list selected
					if(listSearch() > 0) {
						showListResult();
					}
				} else {
					immediateSearch();
				}
				enableGraphics(true);
			}
		});
		GridBagConstraints gbc_btnSearch = new GridBagConstraints();
		gbc_btnSearch.fill = GridBagConstraints.HORIZONTAL;
		gbc_btnSearch.gridwidth = 2;
		gbc_btnSearch.insets = new Insets(2, 2, 5, 5);
		gbc_btnSearch.gridx = 0;
		gbc_btnSearch.gridy = 3;
		frmTexdocGui.getContentPane().add(btnSearch, gbc_btnSearch);
		
		btnClear = new JButton("Clear");
		btnClear.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
				textField.setText("");
				groupMap.get("list").setSelected(true);
//				list.setModel(new DefaultListModel());
//				hideListResult(false);
			}
		});
		GridBagConstraints gbc_btnClear = new GridBagConstraints();
		gbc_btnClear.fill = GridBagConstraints.HORIZONTAL;
		gbc_btnClear.gridwidth = 2;
		gbc_btnClear.insets = new Insets(2, 2, 5, 5);
		gbc_btnClear.gridx = 0;
		gbc_btnClear.gridy = 4;
		frmTexdocGui.getContentPane().add(btnClear, gbc_btnClear);
		
		list = new JList() {
			public String getToolTipText(MouseEvent e) {
				int index = locationToIndex(e.getPoint());
				if (index > -1) {
					return tooltips.getToolTipAt(index);
				} else {
					return null;
				}
			}
			
			public void processMouseEvent(MouseEvent e) {
				super.processMouseEvent(e);
				if(this.getSelectedIndex() > -1) {
					btnOpen.setEnabled(true);
					if(e.getClickCount() == 2) {
						openDocumentAt(list.getSelectedIndex());
					}
				} else {
					btnOpen.setEnabled(false);
				}
			}
		};
		gbc_list = new GridBagConstraints();
		gbc_list.gridheight = 7;
		gbc_list.fill = GridBagConstraints.BOTH;
		gbc_list.gridx = 3;
		gbc_list.gridy = 0;
//		frame.getContentPane().add(list, gbc_list);
		jScrollPane = new JScrollPane(list);
		frmTexdocGui.getContentPane().add(jScrollPane, gbc_list);
		
		JButton btnClose = new JButton("Close");
		btnClose.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg) {
				System.exit(0);
			}
		});
		GridBagConstraints gbc_btnClose = new GridBagConstraints();
		gbc_btnClose.fill = GridBagConstraints.HORIZONTAL;
		gbc_btnClose.gridwidth = 2;
		gbc_btnClose.anchor = GridBagConstraints.SOUTH;
		gbc_btnClose.insets = new Insets(2, 2, 2, 5);
		gbc_btnClose.gridx = 0;
		gbc_btnClose.gridy = 6;
		frmTexdocGui.getContentPane().add(btnClose, gbc_btnClose);
		
		btnOpen = new JButton("Open");
		btnOpen.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg) {
				if(list.getSelectedIndex()>-1) {
					openDocumentAt(list.getSelectedIndex());
				}
			}
		});
		btnOpen.setEnabled(false);
		GridBagConstraints gbc_btnOpen = new GridBagConstraints();
		gbc_btnOpen.fill = GridBagConstraints.HORIZONTAL;
		gbc_btnOpen.gridwidth = 2;
		gbc_btnOpen.insets = new Insets(0, 0, 5, 5);
		gbc_btnOpen.gridx = 0;
		gbc_btnOpen.gridy = 5;
		frmTexdocGui.getContentPane().add(btnOpen, gbc_btnOpen);
	}
	
	private void enableGraphics(boolean enable) {
		textField.setEnabled(enable);
		Enumeration<AbstractButton> group = buttonGroup.getElements();
		while(group.hasMoreElements()) {
			group.nextElement().setEnabled(enable);
		}
		btnClear.setEnabled(enable);
	}
	
	private void showListResult() {
		int width = jScrollPane.getWidth();
		if(!separator.isShowing()) {
			width += separator.getWidth() + 20;
			frmTexdocGui.getContentPane().getLayout().addLayoutComponent("separator", separator);
			frmTexdocGui.getContentPane().add(separator, gbc_separator);
			frmTexdocGui.setResizable(true);
		}
		if(!jScrollPane.isShowing()) {
			frmTexdocGui.getContentPane().getLayout().addLayoutComponent("jScrollPane", jScrollPane);
			frmTexdocGui.getContentPane().add(jScrollPane, gbc_list);
//			frame.setBounds(frame.getX(), frame.getY(), frame.getWidth()+width, frame.getHeight());
			frmTexdocGui.setSize(frmTexdocGui.getWidth()+width, frmTexdocGui.getHeight());
			frmTexdocGui.repaint();
		}
	}
	
	private void hideListResult(boolean showSeparator) {
		int width = jScrollPane.getWidth();
		if(!showSeparator) {
			width += separator.getWidth() + 20;
			frmTexdocGui.getContentPane().getLayout().removeLayoutComponent(separator);
			frmTexdocGui.remove(separator);
			frmTexdocGui.setResizable(false);
		}
		if(jScrollPane.isShowing()) {
			frmTexdocGui.getContentPane().getLayout().removeLayoutComponent(jScrollPane);
			frmTexdocGui.remove(jScrollPane);
//			frame.setBounds(frame.getX(), frame.getY(), frame.getWidth()-width, frame.getHeight());
			frmTexdocGui.setSize(frmTexdocGui.getWidth()-width, frmTexdocGui.getHeight());
			frmTexdocGui.repaint();
		}
	}
	
	private int listSearch() {
		if("".equals(textField.getText())) {
			SimpleLogger.log("Empty search string");
			return 0;
		} else {
			SimpleLogger.log("Searching list for \"" + textField.getText() + "\"");
			toi.executeSearch(textField.getText());
			DefaultListModel dlm = new DefaultListModel();
			tooltips.clear();
			results.clear();
			while(toi.hasNext()) {
				TexdocResult tr = toi.next();
				//insert the result in the list
				dlm.addElement(tr.getLastPartPath());
				//and the tooltip in the list
				tooltips.addToolTip(tr.getInfo(), tr.getPoints());
				//save the result in a list if needed after
				results.add(tr);
			}
			SimpleLogger.log("Found " + dlm.size() + " results");
			list.setModel(dlm);
			
			return list.getModel().getSize();
		}
	}
	
	private void immediateSearch() {
		ProcessBuilder pb = new ProcessBuilder(Arrays.asList(new String[]{"texdoc", "-w", textField.getText()}));
		SimpleLogger.log("Immediate search for \""+textField.getText()+"\"");
		try {
			Process p = pb.start();
			SimpleLogger.log("Immediate search exit with status " + p.waitFor());
		} catch (IOException e) {
			SimpleLogger.log("Exception while starting an immediate search");
			SimpleLogger.log(e.getMessage());
		} catch (InterruptedException e) {
			SimpleLogger.log("Exception while waiting for an immediate search to end");
			SimpleLogger.log(e.getMessage());
		}
	}
	
	private void openDocumentAt(int documentNumberInList) {
		TexdocResult tr = results.get(documentNumberInList);
		ProcessBuilder pb = new ProcessBuilder(Arrays.asList(new String[]{"texdoc", tr.getLastPartPath()}));
		SimpleLogger.log("Opening file " + tr.getFilePath() + " using path " + tr.getLastPartPath());
		try {
			Process p = pb.start();
			SimpleLogger.log("Exit status for subprocess " + p.waitFor());
		} catch (IOException e) {
			SimpleLogger.log("Exception starting texdoc to visualize file.");
			SimpleLogger.log(e.getMessage());
		} catch (InterruptedException e) {
			SimpleLogger.log("Exception while waiting for texdoc exit.");
			SimpleLogger.log(e.getMessage());
		}
	}
}
